#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<cstdlib>
#include<unistd.h>//dup2等
#include<sys/wait.h>
#include<ctype.h>//isspace
//open
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

using namespace std;

//我的全局指令表
const int gargnum=64;
char* gargv[gargnum];
int gargc=0;

//我的全局地址表
//没有"PWD="
const int basesize=1024;
char pwd[basesize];
//有"PWD="
char my_pwd[basesize];

//我的全局环境变量表
const int genvnum=64;
char* genv[genvnum];

//上一指令错误返回值
int lastcode=0;

#define NoneRedir 0
#define InputRedir 1
#define OutputRedir 2
#define AppRedir 3

int redir=NoneRedir;
char* filename=nullptr;

#define TrimSpace(pos) do{\
    while(isspace(*pos)){\
        ++pos;\
    }\
}while(0)

void ResetCommandline(){
    memset(gargv,0,sizeof(gargv));
    gargc=0;

    redir=NoneRedir;
    filename=nullptr;
}

void ParseRedir(char* command_buffer,int len){
    int end=len-1;
    while(end>=0){
        if(command_buffer[end]=='<'){
            redir=InputRedir;
            command_buffer[end]=0;
            filename=&command_buffer[end]+1;
            TrimSpace(filename);
            break;
        }
        else if(command_buffer[end]=='>'){
            if(command_buffer[end-1]=='>'){
                redir=AppRedir;
                command_buffer[end]=0;
                command_buffer[end-1]=0;
                filename=&command_buffer[end]+1;
                TrimSpace(filename);
                break;
            }
            else{
                redir=OutputRedir;
                command_buffer[end]=0;
                filename=&command_buffer[end]+1;
                TrimSpace(filename);
                break;
            }
        }
        else{
            --end;
        }
    }
}
void ParseCommand(char* buffer);
void ParseCommandLine(char* command_buffer,int len){
    ResetCommandline();
    ParseRedir(command_buffer,len);
    ParseCommand(command_buffer);
}

void DoRedir(){
    if(redir==InputRedir){
        if(filename){
            int fd=open(filename,O_RDONLY);
            if(fd<0){
                exit(2);
            }
            dup2(fd,0);
        }
        else
            exit(1);
    }
    else if(redir==OutputRedir){
        if(filename){
            int fd=open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
            //truncate 表示截断
            if(fd<0){
                exit(4);
            }
            dup2(fd,1);
        }
        else
            exit(3);
    }
    else if(redir==AppRedir){
        if(filename){
            int fd=open(filename,O_CREAT|O_WRONLY|O_APPEND,0666);
            if(fd<0){
                exit(6);
            }
            dup2(fd,1);
        }
        else
            exit(5);
    }

}

void InitEnv(){
    extern char** environ;
    int index=0;
//如果文件类型（filetype）被误判（如 .cc 文件被识别为 HTML）
//"<"or">"可能会被判定为HTML标签，
//Vim 会使用错误的语法高亮规则。
   while(environ[index]&&index<=genvnum-2){
        genv[index]=(char*)malloc(strlen(environ[index])+1);
        sprintf(genv[index],"%s",environ[index]);
        ++index;
    }
    genv[index]=nullptr;
}

void DeletEnv(){
    int index=0;
    while(genv[index]){
        free(genv[index]);
        ++index;
    }
}

string GetUser(){
    string user=getenv("USER");
    return user.empty()?"None":user;
}

string GetHostname(){
    string hostname=getenv("HOSTNAME");
    return hostname.empty()?"None":hostname;
}

string GetDir(){
    if(!getcwd(pwd,basesize))
        return "None";

    snprintf(my_pwd,basesize,"PWD=%s",pwd);
    setenv("PWD",pwd,1);

    return pwd;
}

string GetLastDir(){
    string last=GetDir();
    if(last=="/"||last=="None")
        return last;    
    
    size_t pos=last.rfind('/');
    if(pos==string::npos)
        return last;

    return last.substr(pos+1);
}

void PrintCommand(){
    printf("[%s@%s %s]#",
            GetUser().c_str(),
            GetHostname().c_str(),
            GetLastDir().c_str());
}

bool GetCommand(char* buffer,int size){
    char* result=fgets(buffer,size,stdin);
    if(!result)
        return false;

    //一定要记住把\n换成0
    buffer[strlen(buffer)-1]=0;
    //注意只有\n的情况
    if(!strlen(buffer))
        return false;
    return true;
}

void ParseCommand(char* buffer){
   gargv[gargc++]=strtok(buffer," ");
   while((bool)(gargv[gargc++]=strtok(nullptr," ")));
   --gargc;
}

void test(){
    for(int i=0;gargv[i];i++){
        printf("%s ",gargv[i]);
    }
}

bool AddEnv(const string& var){
    size_t  pos=var.find('=');
    if(pos==string::npos)
        return false;

    int index=0;
    //num-1(最后一个要置为空) num-2(存在的找有没有相同的)
    while(index<=genvnum-2 && genv[index]){
        //pos+1保证都一直到等号都相等
        if(strncmp(genv[index],var.c_str(),pos+1)==0){
            free(genv[index]);
            break;
        }
        ++index;
    }

    //num-1为空，num-2为新加入的一开始为空
    if(index<=genvnum-2){
        if(!genv[index]){
            genv[index+1]=0;
        }

        //替换or末尾加值
        genv[index]=(char*)malloc(var.size()+1);
        sprintf(genv[index],"%s",var.c_str());
    }

    //避免使用 var.c_str() 直接传入
    //因为 c_str() 返回的指针指向 std::string 内部的临时内存，
    //可能被析构或修改
    //导致 putenv 访问无效数据
    //putenv(var.c_str());
    //
    //char* c_var=(char*)malloc(var.size()+1);
    //if(!c_var){
    //    perror("c_var malloc failed\n");
    //    return false;
    //}
    //strcpy(c_var,var.c_str());
    //putenv(c_var);
    

    char* name=(char*)malloc(pos+1);
    if(!name){
        perror("name of addenv malloc failed\n");
        return false;
    }
    snprintf(name,pos+1,"%s",var.c_str());
    char* c_var=(char*)malloc(var.size()-pos+1);
    if(!c_var){
        perror("c_var of addenv malloc failed\n");
        return false;
    }
    snprintf(c_var,var.size()-pos+1,"%s",var.c_str()+pos+1);
    setenv(name,c_var,1);
    free(name);
    free(c_var);

    return true;
}

bool ChDir(const string& dir){
    if(chdir(dir.c_str())){//成功返回0
        perror("chdir failed\n");
        return false;
    }

    if(!getcwd(pwd,basesize)){
        perror("getcwd failed\n");
        return false;
    }

    snprintf(my_pwd,basesize,"PWD=%s",pwd);
    if(!AddEnv(my_pwd)){
        perror("AddEnv failed\n");
        return false;
    }
    return true;
}

void MyEcho(){
    for(int i=1;i<=gargc-1;i++){
        if(gargv[i][0]=='$'){
            if(gargv[i][1]=='?'){
                printf("%d ",lastcode);
            }
            else{
                char* var=getenv(gargv[i]+1);
                printf("%s%s",(var?var:" "),(i<=gargc-2?" ":""));
            }
        }
        else{
            printf("%s ",gargv[i]);
        }   

    }       
    printf("\n");
}

bool ExecuteCommand();
//内接命令：chdir(改变当前进程路径)
//          export(可设计当前环境变量)
//          echo(输出当前环境变量)
//          env(输出时用内接，修改时用外接)
bool CheckAndBuildInerCom(){
    if(strcmp(gargv[0],"cd")==0){
        if(gargc==2){
            if(!ChDir(gargv[1]))
                lastcode=1;
        }
        else
            lastcode=1;
        return true;
    }
    else if(strcmp(gargv[0],"export")==0){
        if(gargc>=2){
            for(int i=1;i<=gargc-1;i++){
                 if(!AddEnv(gargv[i]))
                     lastcode=2;
            }
        }
       else 
            lastcode=2;
        return true;
    }
    else if(strcmp(gargv[0],"echo")==0){
        if(gargc>=2)
            MyEcho();
        else 
            lastcode=3;
        return true;
    }
    else if(strcmp(gargv[0],"env")==0){
        if(gargc==1) {
            int i=0;
            while(genv[i]!=nullptr){
                printf("%s\n",genv[i]);
                ++i;
            }
        }
        else
            lastcode=ExecuteCommand()==true?0:4;
        return true;
    }
    return false;
}

//外接命令
bool ExecuteCommand(){
    pid_t id=fork();
    if(id<0){
        perror("fork failed!");
        return false;
    }
    else if(id==0){
        DoRedir();
        execvpe(gargv[0],gargv,genv);
        exit(7);
    }
    else{
        int status=0;
        pid_t chid=waitpid(id,&status,0);
        if(chid<0){
            perror("waitpid failed");
            return false;
        }
        else if(chid>0){
            if(WIFEXITED(status)){
                lastcode=WEXITSTATUS(status);
            }
            else{
                lastcode=100;
            }
            return true;
        }
    }

    return false;
}

int main(){
    char command_buffer[basesize];
    InitEnv();
    while(1){
        PrintCommand();
        if(!GetCommand(command_buffer,basesize))
            continue;

        ParseCommandLine(command_buffer,strlen(command_buffer));
        //test();
        if(CheckAndBuildInerCom())
            continue;

        ExecuteCommand();
    }
    DeletEnv();
    return 0;
}
